<link rel="import" href="../../bower_components/polymer/polymer.html">

<!--
An element which provides a filter parsing for pbtxt to graph output.
-->
<dom-module id="tf-graph-loader">
</dom-module>

<script>
Polymer({

  is: 'tf-graph-loader',

  properties: {
    /**
     * @type {value: number, msg: string}
     *
     * A number between 0 and 100 denoting the % of progress
     * for the progress bar and the displayed message.
     */
    progress: {
      type: Object,
      notify: true,
      readOnly: true // Produces, does not consume.
    },
    datasets: Array,
    hasStats: {
      type: Boolean,
      readOnly: true, // This property produces data.
      notify: true
    },
    selectedDataset: Number,
    selectedFile: {
      type: Object,
      observer: '_selectedFileChanged'
    },
    outGraphHierarchy: {
      type: Object,
      readOnly: true, //readonly so outsider can't change this via binding
      notify: true
    },
    outGraph: {
      type: Object,
      readOnly: true, //readonly so outsider can't change this via binding
      notify: true
    },
    outGraphName: {
      type: String,
      readOnly: true,
      notify: true
    }
  },
  observers: [
    '_selectedDatasetChanged(selectedDataset, datasets)'
  ],
  _parseAndConstructHierarchicalGraph: function(dataset, pbTxtContent) {
    var self = this;
    // Reset the progress bar to 0.
    self._setProgress({
      value: 0,
      msg: ''
    });
    var tracker = {
      setMessage: function(msg) {
        self._setProgress({
          value: self.progress.value,
          msg: msg
        });
      },
      updateProgress: function(value) {
        self._setProgress({
          value: self.progress.value + value,
          msg: self.progress.msg
        });
      },
      reportError: function(msg) {
        self._setProgress({
          value: self.progress.value,
          msg: msg,
          error: true
        });
      },
    };
    var statsJson;
    var dataTracker = tf.getSubtaskTracker(tracker, 30, 'Data');
    tf.graph.parser.readAndParseData(dataset, pbTxtContent, dataTracker)
    .then(function(result) {
      // Build the flat graph (consists only of Op nodes).
      var nodes = result.nodes;
      statsJson = result.statsJson;

      // This is the whitelist of inputs on op types that are considered
      // reference edges. "Assign 0" indicates that the first input to
      // an OpNode with operation type "Assign" is a reference edge.
      var refEdges = {};
      refEdges["Assign 0"] = true;
      refEdges["AssignAdd 0"] = true;
      refEdges["AssignSub 0"] = true;
      refEdges["assign 0"] = true;
      refEdges["assign_add 0"] = true;
      refEdges["assign_sub 0"] = true;
      refEdges["count_up_to 0"] = true;
      refEdges["ScatterAdd 0"] = true;
      refEdges["ScatterSub 0"] = true;
      refEdges["ScatterUpdate 0"] = true;
      refEdges["scatter_add 0"] = true;
      refEdges["scatter_sub 0"] = true;
      refEdges["scatter_update 0"] = true;
      var buildParams = {
        enableEmbedding: true,
        inEmbeddingTypes: ['Const'],
        outEmbeddingTypes: ['^[a-zA-Z]+Summary$'],
        refEdges: refEdges
      };
      var graphTracker = tf.getSubtaskTracker(tracker, 20,
          'Graph');
      return tf.graph.build(nodes, buildParams, graphTracker);
    })
    .then(function(graph) {
      this._setOutGraph(graph);
      if (statsJson) {
        // If there are associated stats, join them with the graph.
        tf.time('Joining stats info with graph...', function() {
          tf.graph.joinStatsInfoWithGraph(graph, statsJson);
        });
      }
      var hierarchyParams = {
        verifyTemplate: true,
        // If a set of numbered op nodes has at least this number of nodes
        // then group them into a series node.
        seriesNodeMinSize: 5,
      };
      var hierarchyTracker = tf.getSubtaskTracker(tracker, 50,
          'Namespace hierarchy');
      return tf.graph.hierarchy.build(graph, hierarchyParams, hierarchyTracker);
    }.bind(this))
    .then(function(graphHierarchy) {
      // Update the properties which notify the parent with the
      // graph hierarchy and whether the data has live stats or not.
      this._setHasStats(statsJson != null);
      this._setOutGraphHierarchy(graphHierarchy);
    }.bind(this))
    .catch(function(reason) {
      tracker.reportError("Graph visualization failed: " + reason);
    });
  },
  _selectedDatasetChanged: function(datasetIndex, datasets) {
    var dataset = datasets[datasetIndex];
    this._parseAndConstructHierarchicalGraph(dataset);
    this._setOutGraphName(dataset.name);
  },
  _selectedFileChanged: function(e) {
    if (!e) {
      return;
    }
    var file = e.target.files[0];
    if (!file) {
      return;
    }

    // Clear out the value of the file chooser. This ensures that if the user
    // selects the same file, we'll re-read it.
    e.target.value = '';

    var reader = new FileReader();

    reader.onload = function(e) {
      this._parseAndConstructHierarchicalGraph(null, e.target.result);
    }.bind(this);

    reader.readAsText(file);
  }
});
</script>
